#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <vector>
#include <string>
#include <random>
#include <math.h>
using namespace cv;
using namespace std;
int cv_flip_photo()
{
    cv::Mat img,outputimg;
    img = cv::imread("./doc/unchange/meinv01.webp",cv::IMREAD_COLOR);

    if(img.empty())
    {
        perror("read img error!!\n");
        return 0;
    }

    // 翻转   > 0 水平  = 0 垂直  < 0  水平和垂直 
    // cv::flip(img,outputimg,1);
    // cv::flip(img,outputimg,0);
    cv::flip(img,outputimg,-1);

    cv::imshow("img", outputimg);
    cvWaitKey(0);
    return 0;

}
/*
 @Mat: 存放矩阵的一种数据结构;图像就存放在这里
*/

int mat_3_3()
{
    // 3 * 3 矩阵  
    cv::Matx33d rix( 1.0,2.0,3.0,
                    4.0,5.0,6.0,
                    7.0,8.0,9.0 );
    // 3 * 1 矩阵   向量
    cv::Matx31d vector(5.0, 1.0,3.0);
    // [3 * 3] * [3 * 1]  == [3 * 1]
    cv::Matx31d result = rix * vector;

    
}

int cat_icon()
{
    cv::Mat imgback = cv::imread("./doc/unchange/daotian.jpeg",cv::IMREAD_COLOR);
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    // 获取背景图的ROI区域
    cv::Mat imgROI(imgback,cv::Rect((imgback.cols - img.cols)/2,(imgback.rows - img.rows)/2 , img.cols,img.rows));
    cv::Mat mask;
    cv::cvtColor(img,mask,cv::COLOR_BGR2GRAY);   //灰度图片
    cv::imshow("mask",mask);

    img.copyTo(imgROI,mask);   // 将图标插入到ROI区域
    // cv::imshow("imgROI",imgROI);
    cv::imshow("imgROI",imgback);
    cv::waitKey(0);
    return 0;
}

void first_option(std::string cv_num)
{
    if(cv_num == "1.1")
    {
        cv_flip_photo();
    } else if(cv_num == "1.2")
    {
        cat_icon();
    } else if(cv_num == "1.3")
    {
        
    } else if(cv_num == "1.4")
    {
        
    } else if(cv_num == "1.5")
    {
        
    }
}

/*
 @ 获取一幅图像的像素值，可以通过迭代器，指针，at<>()来进行获取，然后处理0～255的数据；
 @ 图片本质上就是一个矩阵
*/


// 将图片上的随机点变成白点     添加椒盐噪声
void xiangsu_scan()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    std::default_random_engine aaa;   // 随机数对象
    std::uniform_int_distribution<int>randomRow(0, img.rows - 1);   // 随机数范围
    std::uniform_int_distribution<int>randomCol(0, img.cols - 1);
    int i = 0,j = 0;
    for(int k = 0 ; k < 600;k++)
    {
        i = randomRow(aaa);
        j = randomCol(aaa);
        if(img.type() == CV_8UC1)
        {
            img.at<uchar>(i,j) = 255;
        }
        else if (img.type() == CV_8UC3) {
            img.at<cv::Vec3b>(i,j)[0] = 255;     // at<...>( , )速度最慢，效率底
            img.at<cv::Vec3b>(i,j)[1] = 255;
            img.at<cv::Vec3b>(i,j)[2] = 255;
        }
    }
    cv::imshow("img",img);
    cv::waitKey(0);
}


//  减色函数 256 * 256 * 256 种颜色 减少成 32 * 32 * 32
int point_scan()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    /* 获取行数和列数 */
    int col = img.cols;
    int n_row = img.rows * img.channels();    // 获取一行的数据总数
    for(int j = 0; j < col * 2 / 3; j++)
    {
        uchar * temp = img.ptr<uchar>(j);    //  获取每一行的地址
        for(int i = 0; i < n_row; i++)
        {
            /* 采用指针遍历是最快的 */
            temp[i] = (temp[i] / 8) * 8 + 4;      // 将颜色的256 分成 32个等级，并逢4进1
        }
    }
    cv::imshow("img",img);
    cv::waitKey(0);
    return 0;
    
}
// 迭代器获取像素点
int iterator_scan()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    /* 获取行数和列数 */
    for(cv::Mat_<cv::Vec3b>::iterator iter = img.begin<cv::Vec3b>(); iter != img.end<cv::Vec3b>();iter++)
    {
        (*iter)[0] &= 0xf0;
        (*iter)[0] += 8;
        (*iter)[1] &= 0xf0;
        (*iter)[1] += 8;
        (*iter)[2] &= 0xf0;
        (*iter)[2] += 8;     // 迭代器优点就是不容易出错，但是效率比较慢
    }
    cv::imshow("img",img);
    cv::waitKey(0);
    return 0;
}

// 高效的图像扫描循环
/* 计算机运算效率： 按位运算  >  取模运算  >  整数运算 */



/* 
 @：锐化图像 --------  基于拉普拉斯算子的
 @:如果图像中间去拉普拉斯算子部分，图像的边缘就会被放大；就会实现锐化效果
 @： current = 5*current-left-right-up-down;
 @： 利用矩阵表示就是 ： 『 0 -1 0 | -1 5 -1 | 0 -1 0 』
*/
int laplas_scan()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result; 
    result.create(img.size(),img.type());
    int m_Row = img.rows;
    int m_col = img.cols;
    int m_channel = img.channels();   // 获取通道数，这样就可以知道每一行第二个像素起始位置了
    // 第一行和最后一行是没有相邻的像素的
    for(int i = 1; i < m_Row - 1;i++)
    {
        uchar * last_row = img.ptr<uchar>(i - 1);
        uchar * curremt_row = img.ptr<uchar>(i);
        uchar * next_row = img.ptr<uchar>(i + 1);
        uchar * output = result.ptr<uchar>(i);
        for(int j = m_channel; j < (m_col - 1) * m_channel; j++)
        {
            output[j] = cv::saturate_cast<uchar>(curremt_row[j] * 5 - last_row[j] - next_row[j] 
                        - curremt_row[j - m_channel] - curremt_row[j + m_channel]);
        }
    }
    result.row(0).setTo(cv::Scalar(0));
    result.row(m_Row - 1).setTo(cv::Scalar(0));
    result.col(0).setTo(cv::Scalar(0));
    result.col(m_col - 1).setTo(cv::Scalar(0));
    cv::imshow("result",result);
    cv::waitKey(0);
    return 0;
}

// 图像相加-----图像重叠
int add_scan()
{

    double alpha = 0.5;   // G(x)= (1 - \alpha)f_0(x)+ \alpha f_1(x)
    double beta = 0;
    cv::Mat img = cv::imread("./doc/unchange/meinv01.webp", cv::IMREAD_COLOR);
    cv::Mat backimg = cv::imread("./doc/unchange/meinv02.jpeg", cv::IMREAD_COLOR);
    cv::Mat dst;
    // dst = backimg.clone();
    // 图像混合
    if(img.empty() || backimg.empty())
    {
        std::cout << "photo load error!!!" << std::endl;
        return 0;
    }
    beta = 1 - alpha;
    cv::resize(backimg,backimg,cv::Size(img.cols,img.rows));    // 融合的图像大小必须是一致的，所以需要调整图像大小
    cv::namedWindow("dst");
    cv::createTrackbar("ALPHA","dst",0,100);
    while(1)
    {
        alpha = cv::getTrackbarPos("ALPHA","dst") / 100.0;
        beta = 1 - alpha;
        cv::addWeighted(img,alpha,backimg,beta,0.0,dst);    // 两张图像进行不同权重融合
        cv::imshow("dst",dst);
        if(cv::waitKey(1) == 'q')
        {
            break;
        }
    }
    // cv::add(img,backimg,dst);
    return 0;
}


// 图像重映射--------移动像素，改变外观
int remap_scan()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result; 
    // 映射参数
    cv::Mat srcX(img.rows,img.cols,CV_32F);
    cv::Mat srcY(img.rows,img.cols,CV_32F);
    for(int i = 0; i < img.rows; i++)
    {
        for(int j = 0; j < img.cols; j++)
        {
            srcX.at<float>(i,j) = j;
            srcY.at<float>(i,j) = i + 5 * sin(j / 10.0);  // 行像素以sin函数移动
        }
    }
    /* 使用映射参数 */
    cv::remap(img,result,srcX,srcY,cv::INTER_LINEAR);
    cv::imshow("result",result);
    cv::waitKey(0);
    return 0;
}
/*  像素处理 */
void secend_option(std::string cv_num)
{
    if(cv_num == "2.1")
    {
        xiangsu_scan();
    } else if(cv_num == "2.2")
    {
        point_scan();
    } else if(cv_num == "2.3")
    {
        iterator_scan();
    } else if(cv_num == "2.4")
    {
        laplas_scan();
    } else if(cv_num == "2.5")
    {
        add_scan();
    } else if(cv_num == "2.6")
    {
        remap_scan();
    } else if(cv_num == "2.7")
    {
        ;
    }
}

// 策略模式比较颜色
int mode_color()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat hsv;
    cv::Mat rz_img;
    cv::cvtColor(img,hsv,cv::COLOR_BGR2HLS);
    cv::inRange(hsv,cv::Scalar(58,7,43),cv::Scalar(147,65,255),rz_img);
    cv::imshow("img",rz_img);
    cv::waitKey(0);
    return 0;
}

/* GrabCut 分割图像 */
int GrabCut_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Rect rectrange(400,300,700,700);
    cv::Mat result;             // 分割结果(四种可能的值)
    cv::Mat bgModel,fgModel;        // 模型(内部使用)
    // GrabCut 分割算法
     cv::grabCut(img, result,
    rectrange,         // 包含前景的矩形
    bgModel,fgModel,   // 模型
    5,             // 迭代次数
    cv::GC_INIT_WITH_RECT);   // 使用矩形

    // 取得标记为“可能属于前景”的像素
    cv::compare(result,cv::GC_PR_FGD,result,cv::CMP_EQ);
    // 生成输出图像
    cv::Mat foreground(img.size(),CV_8UC3,cv::Scalar(255,255,255));
    img.copyTo(foreground, result); // 不复制背景像素
    result= result&1; // 如果是前景像素,结果为 1
    // cv::cvtColor(img,hsv,cv::COLOR_BGR2HLS);
    // cv::inRange(hsv,cv::Scalar(58,7,43),cv::Scalar(147,65,255),rz_img);
    cv::imshow("img",result);
    cv::waitKey(0);
    return 0;
}


/* 转换颜色 */
int change_color()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat m_lab;
    // cv::Mat m_one_chonnel;
    // m_one_chonnel.create(img.rows, img.cols,CV_8U);
    cv::cvtColor(img,m_lab,CV_BGR2Lab);       /* 颜色转换接口 */

    cv::imshow("img",m_lab);
    cv::waitKey(0);
    return 0;

 
}


/* 用 色度（主色） 饱和度（鲜艳颜色） 亮度（光亮程度）  表示颜色 ------ 提取图像的亮度 色度 饱和度 */
int hlv_color()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat hsv;
    cv::cvtColor(img,hsv,CV_BGR2HSV);       /* 颜色转换接口 */

    std::vector<cv::Mat> channels;
    /*
        // channels[0]是色调
        // channels[1]是饱和度
        // channels[2]是亮度
    */
    cv::split(hsv,channels);
    // 所有像素的颜色亮度通道将变成 255
    channels[1]= 255;
    // 重新合并通道
    cv::merge(channels,hsv);
    // 转换回 BGR
    cv::Mat newImage;
    cv::cvtColor(hsv,newImage,CV_HSV2BGR);

    cv::imshow("newImage",newImage);

    cv::waitKey(0);
    return 0;
}




/* 颜色处理 */
void third_option(std::string cv_num)
{
    if(cv_num == "3.1")
    {
        mode_color();
    } else if(cv_num == "3.2")
    {
        GrabCut_img();
    } else if(cv_num == "3.3")
    {
        change_color();
    } else if(cv_num == "3.4")
    {
        hlv_color();
    } else if(cv_num == "3.5")
    {
        ;
    }
}


/* 计算图像直方图 : 得到图像中每种像素值的个数 */
/*
 @读取图像 ------  分离图像  ---- 设置范围参数  ---- 获取图像直方图 ----- 划线表示直方图每个像素点数量
*/
int Histogra_img()
{
    using namespace cv; 
    cv::Mat img = cv::imread("./doc/unchange/meinv01.webp",cv::IMREAD_COLOR);
    if(img.empty())
    {
        perror("file open fail\n");
        return -1;
    }
    std::vector<cv::Mat> bgr_planes;
    cv::split(img,bgr_planes);       // R G B 平面中分离图像;
    int histSize = 256;              // 建立箱数 
    float range[] = { 0, 256 } ;     // 设置范围
    const float* histRange = { range };
    bool uniform = true; bool accumulate = false;    // 设置箱子尺寸均匀  开始时清除直方图
    cv::Mat b_hist, g_hist, r_hist;   // 保存直方图的对象
    /* 计算直方图  **源数组（s） 源数组的数量  要测量的通道  在源数组上使用的掩码  要存储直方图的对象 直方图维度 每个使用的维数的数量 每个维度要测量的值的范围  纸箱尺寸相同 直方图在开始时清除 */
    cv::calcHist( &bgr_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
    cv::calcHist( &bgr_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
    cv::calcHist( &bgr_planes[2], 1, 0, cv::Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );

    int hist_w = 1024; int hist_h = 400;
    int bin_w = cvRound( (double) hist_w/histSize );    // 将窗口分成 256 份；一种像素值占一份
    // int bin_w = 4;
    cv::Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );    // 创建背景 
    /* 做归一化处理 */
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
    // 用每个点的数量做Y轴3值；将他们连起来
    for( int i = 1; i < histSize; i++ )
    {
        line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ) ,
                        Point( (bin_w*(i) + bin_w*(i-1))/2, hist_h - cvRound((b_hist.at<float>(i) + b_hist.at<float>(i-1))/ 2.0) ),
                        Scalar( 255, 0, 0), 2, 8, 0  );
        line( histImage, Point( (bin_w*(i) + bin_w*(i-1))/2, hist_h - cvRound((b_hist.at<float>(i) + b_hist.at<float>(i-1))/ 2.0) ) ,
                        Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
                        Scalar( 255, 0, 0), 2, 8, 0  );
        line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ) ,
                        Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
                        Scalar( 0, 255, 0), 2, 8, 0  );
        line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ) ,
                        Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                        Scalar( 0, 0, 255), 2, 8, 0  );
    }
    namedWindow("calcHist Demo", WINDOW_AUTOSIZE );
    imshow("calcHist Demo", histImage );
    waitKey(0);
    return 0;
}




/* 利用查找表修改图像外观 */
cv::Mat reversePixel(const cv::Mat& image,int tidu)
{
    cv::Mat lut(256, 1, CV_8UC1);    // 定义查找表
    cv::Mat result;
    for (int i = 0; i < 256; i++)
    {
        lut.at<uchar>(i) = (255 - i)/tidu ;     // 查找表反转
    }
    cv::LUT(image, lut, result);   // 通过查找表进行颜色反转
    return result;
}
int cat_table_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat out1 = reversePixel(img,1);
    cv::Mat out2 = reversePixel(img,2);
    cv::Mat out3 = reversePixel(img,10);

    cv::imshow("out1",out1);
    cv::imshow("out2",out2);
    cv::imshow("out3",out3);
    cv::waitKey(0);
}

/* 直方图均衡化 ----------   使直方图的值分布更加的均匀化  */
int junheng_img()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat dst;
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    cv::equalizeHist( img, dst );   // 均衡图像直方图
    int histSize = 256;              // 建立箱数 
    float range[] = { 0, 256 } ;     // 设置范围
    const float* histRange = { range };
    bool uniform = true; bool accumulate = false;    // 设置箱子尺寸均匀  开始时清除直方图
    cv::Mat hist;   // 保存直方图的对象
    cv::calcHist( &dst, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
    normalize(hist, hist, 0, dst.rows, NORM_MINMAX, -1, Mat() );
    for( int i = 1; i < histSize; i++ )
    {
        line( dst, Point( 2*(i-1), dst.rows - cvRound(hist.at<float>(i-1)) ) ,
                        Point( 2*(i), dst.rows - cvRound(hist.at<float>(i)) ),
                        Scalar( 255, 0, 0), 2, 8, 0  );
    }
    cv::imshow("junheng",dst);

    cv::waitKey(0);
    return 0;
}




/* 反向投影直方图检测特定图像内容 */
int jiance_img()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat imgRoi;
    imgRoi = img(cv::Rect(250,250,100,100));
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    cv::cvtColor( imgRoi, imgRoi, cv::COLOR_BGR2GRAY );
    int histSize = 256;              // 建立箱数
    float range[] = { 0, 256 } ;     // 设置范围
    const float* histRange = { range };
    bool uniform = true; bool accumulate = false;    // 设置箱子尺寸均匀  开始时清除直方图
    cv::Mat hist;   // 保存直方图的对象
    cv::Mat Roi_hist;
    cv::calcHist( &imgRoi, 1, 0, cv::Mat(), Roi_hist, 1, &histSize, &histRange, uniform, accumulate );
    // cv::calcHist( &img, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
    normalize(Roi_hist, Roi_hist, 0, imgRoi.rows, NORM_MINMAX, -1, Mat() );    // 归一化，得到概率
    // normalize(hist, hist, 0, img.rows, NORM_MINMAX, -1, Mat() );
    cv::Mat back_Hist;
    // 将输入图像替换成对应的概率值
    cv::calcBackProject(&img, 1, 0, Roi_hist, back_Hist, &histRange, 255.0 );
    // 做阈值处理 
    cv::threshold(back_Hist, back_Hist, 125, 255, cv::THRESH_BINARY);

    cv::imshow("jiance",back_Hist);

    cv::waitKey(0);
    return 0;
}



/* 用均值平移算法查找目标 */
/*
    读入第一张狒狒图像，并用矩形区域标记脸部区域。
    得到脸部区域的一维色调直方图，不统计低饱和度的像素。
    输入第二张狒狒图，并转为HSV色彩空间，对第一张狒狒脸部的色调直方图进行反向映射，得到概率分布图。
    定义迭代规则，用均值平移算法查找目标(参数有：概率分布图，原矩形区域，规则)。
    标记新矩形区域。
*/
int cat_face()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat imgRoi;
    cv::Rect rect = cv::Rect(250,250,100,100);
    imgRoi = img(rect);
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    cv::cvtColor( imgRoi, imgRoi, cv::COLOR_BGR2GRAY );
    int histSize = 256;              // 建立箱数
    float range[] = { 0, 256 } ;     // 设置范围
    const float* histRange = { range };
    bool uniform = true; bool accumulate = false;    // 设置箱子尺寸均匀  开始时清除直方图
    cv::Mat hist;   // 保存直方图的对象
    cv::Mat Roi_hist;
    cv::calcHist( &imgRoi, 1, 0, cv::Mat(), Roi_hist, 1, &histSize, &histRange, uniform, accumulate );
    // cv::calcHist( &img, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
    normalize(Roi_hist, Roi_hist, 0, imgRoi.rows, NORM_MINMAX, -1, Mat() );    // 归一化，得到概率
    // normalize(hist, hist, 0, img.rows, NORM_MINMAX, -1, Mat() );
    cv::Mat back_Hist;
    // 将输入图像替换成对应的概率值
    cv::calcBackProject(&img, 1, 0, Roi_hist, back_Hist, &histRange, 255.0 );

	//标记原矩形区域
	cv::rectangle(img, rect, cv::Scalar(0, 0, 255));

	//定义迭代规则
	cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
		10,     //最多迭代10次
		1);     //或者重心移动距离小于1个像素

	//标记新矩形区域
	cv::rectangle(img, rect, cv::Scalar(0, 255, 0));

    cv::imshow("jiance",img);

    cv::waitKey(0);
    return 0;
}




/* 比较直方图搜索相似图片 */
/*
    为了比较两个直方图( H1 和 H2 )，首先我们必须选择度量( d(H1,H2)来表示两个直方图的匹配度。
    比较两副图像的相似度
*/
int sample_img()
{
    using namespace cv;
    cv::Mat img_1 = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat img_2 = cv::imread("./doc/unchange/meinv01.webp",cv::IMREAD_COLOR);
    cv::Mat img_1_HSV,img_2_HSV;
    cv::Mat hsv_down;
    cvtColor( img_1, img_1_HSV, COLOR_BGR2HSV );
    cvtColor( img_2, img_2_HSV, COLOR_BGR2HSV );
    /* 创建一个一半的基本图像 */
    hsv_down = img_1_HSV( Range( img_1_HSV.rows/2, img_1_HSV.rows - 1 ), Range( 0, img_1_HSV.cols - 1 ) );
    int h_bins = 50; int s_bins = 60;
    int histSize [] = {h_bins, s_bins};
    float h_ranges [] = {0, 180};
    float s_ranges [] = {0, 256};
    const  float * ranges [] = {h_ranges, s_ranges};
    int channels [] = {0,1};
    /* 用来存储直方图 */
    MatND hist_half_down;    // 原来图像半的那个
    MatND hist_test1;        // 转换成HSV的img1
    MatND hist_test2;        // 转换成HSV的img2
    calcHist( &hsv_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false );
    normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() );
    calcHist( &img_1_HSV, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
    normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
    calcHist( &img_2_HSV, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false );
    normalize( hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat() );

    for( int i = 0; i < 4; i++ )
    { 
        int compare_method = i;
        double base_half = compareHist( hist_test1, hist_half_down, compare_method );
        double base_test1 = compareHist( hist_test1, hist_test1, compare_method );
        double base_test2 = compareHist( hist_test1, hist_test2, compare_method );
        printf( " Perfect, Base-Half, Base-Test(1), Base-Test(2) : %d, %f, %f, %f \n", i, base_half , base_test1, base_test2 );
    }
    return 0;

}

/* 用积分图像统计像素 */
int jifen_img()
{

}






/* 直方图统计像素 */
void fourty_option(std::string cv_num)
{
    if(cv_num == "4.1")
    {
        Histogra_img();
    } else if(cv_num == "4.2")
    {
        cat_table_img();
    } else if(cv_num == "4.3")
    {
        junheng_img();
    } else if(cv_num == "4.4")
    {
        jiance_img();
    } else if(cv_num == "4.5")
    {
        cat_face();
    } else if(cv_num == "4.6")
    {
        sample_img();
    } else if(cv_num == "4.7")
    {
        jifen_img();
    }
}


/* 腐蚀和膨胀 ----- 处理椒盐噪声有用 */
int fusi_pengzhang()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat imgRoi;
    imgRoi = img(cv::Rect(250,250,100,100));
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    cv::cvtColor( imgRoi, imgRoi, cv::COLOR_BGR2GRAY );
    int histSize = 256;              // 建立箱数
    float range[] = { 0, 256 } ;     // 设置范围
    const float* histRange = { range };
    bool uniform = true; bool accumulate = false;    // 设置箱子尺寸均匀  开始时清除直方图
    cv::Mat hist;   // 保存直方图的对象
    cv::Mat Roi_hist;
    cv::calcHist( &imgRoi, 1, 0, cv::Mat(), Roi_hist, 1, &histSize, &histRange, uniform, accumulate );
    // cv::calcHist( &img, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
    normalize(Roi_hist, Roi_hist, 0, imgRoi.rows, NORM_MINMAX, -1, Mat() );    // 归一化，得到概率
    // normalize(hist, hist, 0, img.rows, NORM_MINMAX, -1, Mat() );
    cv::Mat back_Hist;
    // 将输入图像替换成对应的概率值
    cv::calcBackProject(&img, 1, 0, Roi_hist, back_Hist, &histRange, 255.0 );
    // 做阈值处理 
    cv::threshold(back_Hist, back_Hist, 125, 255, cv::THRESH_BINARY);

    cv::imshow("jiance",back_Hist);
    // 默认 3*3 结构
    cv::Mat img_erod;
    cv::Mat img_dilat;
    cv::erode(back_Hist,img_erod,cv::Mat());   // 腐蚀
    cv::dilate(back_Hist,img_dilat,cv::Mat());    // 膨胀
    cv::imshow("img",img);
    cv::imshow("erod",img_erod);
    cv::imshow("dilat",img_dilat);
    cv::waitKey(0);
    return 0;
}



/* 开启和闭合图像 */
int open_close_img()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    // 默认 3*3 结构
    cv::Mat img_erod;
    cv::Mat img_dilat;
    cv::erode(img,img_erod,cv::Mat());   // 腐蚀  // 去掉小点
    cv::dilate(img_erod,img_erod,cv::Mat());    // 膨胀  放大小点

    cv::dilate(img,img_dilat,cv::Mat());    // 膨胀    放大小点
    cv::erode(img_dilat,img_dilat,cv::Mat());   // 腐蚀     去掉小点

    cv::Mat m_CLOSE;
    cv::Mat element5(5,5,CV_8U,cv::Scalar(1));    // 定义一个 5*5 的锚点区域
    cv:morphologyEx(img_dilat,m_CLOSE,cv::MORPH_CLOSE,element5);

    cv::Mat m_OPEN;
    cv::morphologyEx(img_dilat, m_OPEN, cv::MORPH_OPEN, element5);

     

    
    cv::imshow("erod",m_CLOSE);
    cv::imshow("dilat",m_OPEN);
    cv::waitKey(0);
    return 0;
}





/* 灰度图像的形态学运算 */
int open_grad_img()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::cvtColor( img, img, cv::COLOR_BGR2GRAY );
    cv::Mat element5(5,5,CV_8U,cv::Scalar(1));    // 定义一个 5*5 的锚点区域
    cv::Mat m_grad;
    /* 把灰度图像的值看成梯度，在边缘处变化会大些，即梯度就会大些 */
    cv::morphologyEx(img, m_grad, cv::MORPH_GRADIENT, element5);   // 梯度处理 -- 提取轮廓
    cv::Mat b_hat;
    cv::Mat element7(7, 7, CV_8U, cv::Scalar(1));
    /*  
        顶帽运算也基于图像比对,它使用了开启和闭合运算。因为灰度图像进行形态学开启运算时
        会先对图像进行腐蚀,局部的尖锐部分会被消除,其他部分则将保留下来。因此,原始图像和经
        过开启运算 的 图像的比对结果就是局部的尖锐部分。这些尖锐部分就是我们需要提取的前景物
        体。
    */
    cv::morphologyEx(img, b_hat, cv::MORPH_BLACKHAT, element7);    // 顶帽操作

    cv::imshow("grad",m_grad);
    cv::imshow("blackHat",b_hat);
    cv::waitKey(0);
    return 0;
}

/* 分水岭算法实现分割 */
/*  分水岭变换是一种流行的图像处理算法,用于快速将图像分割成多个同质区域。  */
/*
    如果把图像看作一个拓扑地貌,那么同类区域就相当于陡峭边缘内相对平坦的盆地。分
    水岭算法通过逐步增高水位,把地貌分割成多个部分。
*/

int segmentation_img()
{
    using namespace cv;
    // Load the image
    Mat src = imread("./doc/unchange/meinv02.jpeg");
    // Check if everything was fine
    if (!src.data)
        return -1;
    // Show source image
    imshow("Source Image", src);
    // Change the background from white to black, since that will help later to extract
    // better results during the use of Distance Transform
    for( int x = 0; x < src.rows; x++ ) {
      for( int y = 0; y < src.cols; y++ ) {
          if ( src.at<Vec3b>(x, y) == Vec3b(0,0,0) ) {
            src.at<Vec3b>(x, y)[0] = 255;
            src.at<Vec3b>(x, y)[1] = 255;
            src.at<Vec3b>(x, y)[2] = 255;
          }
        }
    }
    // Show output image
    imshow("Black Background Image", src);
    // 之后，我们将锐化我们的形象，以锐化前景对象的边缘。我们将应用一个具有相当强的滤波器的拉普拉斯滤波器（二阶导数近似）
    Mat kernel = (Mat_<float>(3,3) <<
            1,  1, 1,
            1, -8, 1,
            1,  1, 1); // 二阶导数的近似，一个很强的核
    // do the laplacian filtering as it is   拉普拉斯滤波
    // well, we need to convert everything in something more deeper then CV_8U
    // because the kernel has some negative values,
    // and we can expect in general to have a Laplacian image with negative values
    // BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
    // so the possible negative number will be truncated
    Mat imgLaplacian;
    Mat sharp = src; // copy source image to another temporary one
    filter2D(sharp, imgLaplacian, CV_32F, kernel);      // 使用拉普拉斯滤波器 锐化图像
    src.convertTo(sharp, CV_32F);
    Mat imgResult = sharp - imgLaplacian;
    // convert back to 8bits gray scale
    imgResult.convertTo(imgResult, CV_8UC3);
    imgLaplacian.convertTo(imgLaplacian, CV_8UC3);
    // imshow( "Laplace Filtered Image", imgLaplacian );
    imshow( "New Sharped Image", imgResult );
    src = imgResult; // copy back
    // Create binary image from source image
    /* 新锐的源图像转换为灰度和二进制图像 */
    Mat bw;
    cvtColor(src, bw, CV_BGR2GRAY);
    threshold(bw, bw, 160, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);    // 二值化图像
    imshow("Binary Image", bw);
    // Perform the distance transform algorithm
    Mat dist;
    distanceTransform(bw, dist, CV_DIST_L2, 3);
    // Normalize the distance image for range = {0.0, 1.0}
    // so we can visualize and threshold it
    normalize(dist, dist, 0, 1., NORM_MINMAX);
    imshow("Distance Transform Image", dist);
    // Threshold to obtain the peaks
    // This will be the markers for the foreground objects
    threshold(dist, dist, .4, 1., CV_THRESH_BINARY);
    // Dilate a bit the dist image
    Mat kernel1 = Mat::ones(3, 3, CV_8UC1);
    dilate(dist, dist, kernel1);
    imshow("Peaks", dist);
    // Create the CV_8U version of the distance image
    // It is needed for findContours()
    Mat dist_8u;
    dist.convertTo(dist_8u, CV_8U);
    // Find total markers
    std::vector<std::vector<Point> > contours;
    findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    // Create the marker image for the watershed algorithm
    Mat markers = Mat::zeros(dist.size(), CV_32SC1);
    // Draw the foreground markers
    for (size_t i = 0; i < contours.size(); i++)
        drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i)+1), -1);
    // Draw the background marker
    circle(markers, Point(5,5), 3, CV_RGB(255,255,255), -1);
    imshow("Markers", markers*10000);
    // Perform the watershed algorithm
    watershed(src, markers);
    Mat mark = Mat::zeros(markers.size(), CV_8UC1);
    markers.convertTo(mark, CV_8UC1);
    bitwise_not(mark, mark);
//    imshow("Markers_v2", mark); // uncomment this if you want to see how the mark
                                  // image looks like at that point
    // Generate random colors
    std::vector<Vec3b> colors;
    for (size_t i = 0; i < contours.size(); i++)
    {
        int b = theRNG().uniform(0, 255);
        int g = theRNG().uniform(0, 255);
        int r = theRNG().uniform(0, 255);
        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    }
    // Create the result image
    Mat dst = Mat::zeros(markers.size(), CV_8UC3);
    // Fill labeled objects with random colors
    for (int i = 0; i < markers.rows; i++)
    {
        for (int j = 0; j < markers.cols; j++)
        {
            int index = markers.at<int>(i,j);
            if (index > 0 && index <= static_cast<int>(contours.size()))
                dst.at<Vec3b>(i,j) = colors[index-1];
            else
                dst.at<Vec3b>(i,j) = Vec3b(0,0,0);
        }
    }
    imshow("Final Result", dst);
    waitKey(0);
    return 0;
}

/* 
    用MSER算法提取特征区域 
    最大稳定外部区域(MSER)算法也用相同的水淹类比,以便从图像中提取有意义的区域。创建这些区域时也使用
    逐步提高水位的方法,但是这次我们关注的是在水淹过程中的某段时间内,保持相对稳定的盆地。
    可以发现,这些区域对应着图像中某些物体的特殊部分。
*/
int mser_feat()
{
    cv::Mat src = cv::imread("./doc/unchange/xunyicao.jpeg");
    cv::Ptr<cv::MSER> ptrMSER =
                                cv::MSER::create(5, // 局部检测时使用的增量值
                                    200, // 允许的最小面积
                                    20000); // 允许的最大面积
    // 创建白色图像
    std::vector<std::vector<cv::Point> > points;
    std::vector<cv::Rect> rects;
    ptrMSER->detectRegions(src, points, rects);
    cv::Mat output(src.size(),CV_8UC3);
    output= cv::Scalar(255,255,255);

    // OpenCV 随机数生成器
    cv::RNG rng;
    // 针对每个检测到的特征区域，在彩色区域显示 MSER
    // 反向排序，先显示较大的 MSER
    for (std::vector<std::vector<cv::Point> >::reverse_iterator
        it = points.rbegin();
        it != points.rend(); ++it) {
        // 生成随机颜色
        cv::Vec3b c(rng.uniform(0, 254),
            rng.uniform(0, 254), rng.uniform(0, 254));
        // 针对 MSER 集合中的每个点
        for (std::vector<cv::Point>::iterator itPts = it->begin();
            itPts != it->end(); ++itPts) {
            // 不重写 MSER 的像素
            if (output.at<cv::Vec3b>(*itPts)[0] == 255) {
                output.at<cv::Vec3b>(*itPts) = c;
            }
        }
    }
    cv::imshow("src", src);
    cv::imshow("output", output);
    cv::waitKey(0);
    return 0;

}


// 形态学运算变换图像  

void fivety_option(std::string cv_num)
{
    if(cv_num == "5.1")
    {
        fusi_pengzhang();
    } else if(cv_num == "5.2")
    {
        open_close_img();
    } else if(cv_num == "5.3") 
    {
        open_grad_img();
    } else if(cv_num == "5.4")
    {
        segmentation_img();
    } else if(cv_num == "5.5")
    {
        mser_feat();
    }
}

/* 低通滤波器图像滤波 */
/*
    如果一种滤波器是用邻域像素的加权累加值来替换像素值,我们就说这种滤波器是线性的。
    这里使用了均值滤波器,即将矩形邻域内的全部像素累加,除以该邻域的数量(即求平均值),
    ----------- 图像模糊了 
*/

int lhs_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result;
    // cv::blur(img,result, cv::Size(5,5)); // 滤波器尺寸
                                // // 滤波器尺寸
    cv::GaussianBlur(img, result,cv::Size(5,5),1.5);  // 1.5  ：  控制高斯曲线形状的参数

    cv::imshow("img", result);
    cv::waitKey(0);
    return 0;  
}

/* 缩减像素采样 */
int down_up_sample_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result;
    // 首先去除高频成分
    cv::GaussianBlur(img,img,cv::Size(11,11),2.0);
    // 只保留每 4 个像素中的 1 个
    cv::Mat reduced(img.rows/4,img.cols/4,CV_8U);
    for (int i=0; i<reduced.rows; i++)
        for (int j=0; j<reduced.cols; j++)
            reduced.at<uchar>(i,j)= img.at<uchar>(i*4,j*4);

    cv::Mat resizedImage;           // 用于存储缩放后的图像
    cv::resize(img, resizedImage,cv::Size(img.cols/4,img.rows/4)); // 行和列均缩小为原来的 1/4
    cv::imshow("img", img);
    cv::imshow("resize", resizedImage);
    cv::waitKey(0);
    return 0;  
}

/* 中值滤波器图像滤波  */
int media_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result;
    cv::medianBlur(img, result, 5);     //  中值 滤波 
    cv::imshow("img", result);
    cv::waitKey(0);
    return 0;
}

/* 定向滤波器，边缘检测 */
/*
    Sobel 算子就是图像在垂直和水平方向变化的速度。在数
    学术语中,这种速度称为梯度。
    图像中物体的边缘一般梯度都相对较大 
*/
int sobel_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat result;
    cv::Mat gradX,gradY;
    cv::Sobel(img,gradX, CV_8U, 1, 0,3, 0.4, 128);
    cv::Sobel(img,gradY,CV_8U,0, 1,3, 0.4, 128);
    cv::imshow("gradX", gradX);
    cv::imshow("gradY", gradY);
    cv::waitKey(0);
    return 0;
}


/* 图像的拉普拉斯算子  */
int laplas_cmp()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);


    cv::imshow("img", img);
    cv::waitKey(0);
    return 0;
}


// 图像滤波
void sixty_option(std::string cv_num)
{
    if(cv_num == "6.1")
    {
        lhs_img();
    } else if(cv_num == "6.2")
    {
        down_up_sample_img();
    } else if(cv_num == "6.3") 
    {
        media_img();
    } else if(cv_num == "6.4")
    {
        sobel_img();
    } else if(cv_num == "6.5")
    {
        laplas_cmp();
    }
}



// 用 Canny 算子检测图像轮廓
/*
通过对梯度幅值的阈值化,获得图像中主要边缘的二值分布图：
    通常不可能找到既低到足以检测到图像中所有重要边缘,又高到足以避免产生
太多无关紧要边缘的阈值。这是一个难以权衡的问题。
*/
int canny_img()
{

    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat contours;
    cv::Canny(img,contours,125,350);    // 使用 canny 算法
    cv::imshow("contours", contours);
    cv::waitKey(0);
    return 0;

}



// 用霍夫曼变换检测直线
int hough_img()
{
    const float PI = 3.14;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat contours;
    cv::Canny(img,contours,125,350);    // 使用 canny 算法
    std::vector<cv::Vec2f> lines;
    cv::HoughLines(contours,lines, 1, 3.14/180,60);    // 霍夫曼检测
    std::vector<cv::Vec2f>::const_iterator iter = lines.begin();
    while (iter!=lines.end()) {
        float rho= (*iter)[0];
        // 第一个元素是距离 rho
        float theta= (*iter)[1]; // 第二个元素是角度 theta
        if (theta < PI/4. || theta > 3.*PI/4.) {
            // 直线与第一行的交叉点
            cv::Point pt1(rho/cos(theta),0);
            // 直线与最后一行的交叉点
            cv::Point pt2((rho-contours.rows*sin(theta))/cos(theta),contours.rows);
            // 画白色的线
            cv::line( contours, pt1, pt2, cv::Scalar(255), 1);
        } else { // 水平线(大致)
                // 直线与第一列的交叉点
                cv::Point pt1(0,rho/sin(theta));
                // 直线与最后一列的交叉点
                cv::Point pt2(contours.cols,
                (rho-contours.cols*cos(theta))/sin(theta));
                // 画白色的线
                cv::line(contours, pt1, pt2, cv::Scalar(255), 1);
        }
        ++iter;
    }
    cv::imshow("contours", contours);
    cv::waitKey(0);
    return 0;

}

// 点集的直线拟合-----直线的位置和方向





// 提取轮廓的连续区域
int cannly_tuqu_img()
{
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    std::vector<std::vector<cv::Point>> contours;  // 存储轮廓的向量
    cv::findContours(img,contours,cv::RETR_LIST,cv::CHAIN_APPROX_NONE);
    cv::Mat result(img.size(),CV_8U,cv::Scalar(0));
    cv::drawContours(result,contours,
                        -1, // 画全部轮廓
                        255, // 用黑色画
                        2); // 宽度为 2
    cv::imshow("result", result);
    cv::waitKey(0);
    return 0;
}




// 计算区域的形状描述子






/* 提取直线，轮廓，区域 */
//   从构成图像的像素集中提取出有意义的特征
void eventty_option(std::string cv_num)
{
    if(cv_num == "7.1")
    {
        canny_img();
    } else if(cv_num == "7.2")
    {
        hough_img();
    } else if(cv_num == "7.3") 
    {
        ;
    } else if(cv_num == "7.4")
    {
        cannly_tuqu_img();
    } else if(cv_num == "7.5")
    {
        ;
    }
}

/* 检测兴趣点的角点 ----- 主要是角点的灰度值是重点 */
int test_08_1()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat image;
    //灰度变换  
	cv::cvtColor (img,image,CV_BGR2GRAY);  
	cv::Mat  cornerStrength;  //opencv harris函数检测结果，也就是每个像素的角点响应函数值  
	cv::Mat cornerTh; //cornerStrength阈值化的结果  
	cv::Mat localMax; //局部最大值结果  
	int neighbourhood = 3; //邻域窗口大小  
	int aperture = 3;//sobel边缘检测窗口大小（sobel获取各像素点x，y方向的灰度导数）  
	double k = 0.01 ;  
	double maxStrength = 0.0;//角点响应函数最大值  
	double threshold = 0.01;//阈值除去响应小的值  
	int nonMaxSize = 3;//这里采用默认的3，就是最大值抑制的邻域窗口大小  
	cv::Mat kernel;//最大值抑制的核，这里也就是膨胀用到的核   
    /* 计算角点 */
    //opencv自带的角点响应函数计算函数  
    cv::cornerHarris (image,cornerStrength,neighbourhood,aperture,k);  
    double minStrength;  
    //计算最大最小响应值
    cv::minMaxLoc(cornerStrength,&minStrength,&maxStrength);

    cv::Mat dilated;  
    //默认3*3核膨胀，膨胀之后，除了局部最大值点和原来相同，其它非局部最大值点被  
    //3*3邻域内的最大值点取代  
    cv::dilate (cornerStrength,dilated,cv::Mat());  
    //与原图相比，只剩下和原图值相同的点，这些点都是局部最大值点，保存到localMax  
    cv::compare(cornerStrength,dilated,localMax,cv::CMP_EQ);  

    /* 获取角点*/
    std::vector<cv::Point> pts; 
    cv::Mat cornerMap;  
    // 根据角点响应最大值计算阈值  
    threshold= 0.01 * maxStrength;  
    cv::threshold(cornerStrength,cornerTh,threshold,255,cv::THRESH_BINARY);  
    // 转为8-bit图  
    cornerTh.convertTo(cornerMap,CV_8U);  
    // 和局部最大值图与，剩下角点局部最大值图，即：完成非最大值抑制  
    cv::bitwise_and(cornerMap,localMax,cornerMap);  
    for( int y = 0; y < cornerMap.rows; y++ )
    {  
        const uchar* rowPtr = cornerMap.ptr<uchar>(y);  
        for( int x = 0; x < cornerMap.cols; x++ )
        {  
            // 非零点就是角点  
            if (rowPtr[x]) 
            {  
                pts.push_back(cv::Point(x,y));  
            }  
        }  
    }  

    /* 标记角点 */
    std::vector<cv::Point>::const_iterator it=pts.begin();  
    while (it!=pts.end()) 
    {  
        // 角点处画圈  
        cv::circle(image,*it,3,cv::Scalar(255,255,255),2);  
        ++it;  
    }  

    cv::imshow("image", image);
    cv::waitKey(0);
    return 0;
}
/* 快速检测特征（兴趣点） */
int test_08_2()
{
    using namespace cv;
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    // 用来存储关键点
    std::vector<cv::KeyPoint> keyPoints;
    Ptr<FeatureDetector> m_fast = cv::FastFeatureDetector::create(50);	// 检测的阈值为40
    m_fast->detect(img,keyPoints);
    cv::drawKeypoints(img, keyPoints, img, cv::Scalar::all(255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); 
    cv::imshow("img", img);
    cv::waitKey(0);
    return 0;
}

/* 尺度不变特征的检测 */
/* SURF 特征检测属于 opencv_contrib 库,在编译 OpenCV 时包含了附加模块才能使用 */
int test_08_3()
{
#if 0    /* 缺库 */
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    // 用来存储关键点
    std::vector<cv::KeyPoint> keyPoints;
    // 创建 SURF 特征检测器对象
    cv::Ptr<cv::xfeatures2d::SurfFeatureDetector> ptrSURF =cv::xfeatures2d::SurfFeatureDetector::create(2000.0);
    ptrSURF->detect(img, keyPoints);
    cv::Mat featureImage;
    // 画出关键点,包括尺度和方向信息
    cv::drawKeypoints(img,keyPoints,featureImage,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::imshow("featureImage", featureImage);
    cv::waitKey(0);
#endif
    return 0;
}
/* 多尺度 FAST 特征检测 */
int test_08_4()
{
#if 0    /* 缺库 */
    cv::Mat img = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Ptr<cv::BRISK> ptrBRISK = cv::BRISK::create();    /* 多尺度 FAST 检测*/
    vector<cv::KeyPoint> Keypoints;
    // 检测关键点信息
    ptrBRISK -> detect(image, Keypoints);
    cv::Mat outImage;
    // 画出关键点：包括尺度和方向信息
    cv::drawKeypoints(img,
                    Keypoints,
                    outImage,
                    cv::Scalar(255,255,255),
                    cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS
                    );
    //    这里使用 cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS 标志得到了关键点的圆,并
    //    且圆的尺寸与每个特征计算得到的尺度成正比。为了使特征具有旋转不变性, SURF 还让每个特
    //    征关联了一个方向,由每个圆内的辐射线表示。
    cv::imshow("outImage", outImage);
    cv::waitKey(0);
#endif
    return 0;
}


// 检测兴趣点 *** ---- 目标识别，跟踪 ，图像配准等

void eightty_option(std::string cv_num)
{
    if(cv_num == "8.1")
    {
        test_08_1();
    } else if(cv_num == "8.2")
    {
        test_08_2();
    } else if(cv_num == "8.3") 
    {
        test_08_3();
    } else if(cv_num == "8.4")
    {
       test_08_4();
    } else if(cv_num == "8.5")
    {
        ;
    }
}
/* 局部模板匹配 */
int test_09_1()
{
    using namespace cv;
    cv::Mat img1 = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat img2 = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    if(!(img1.data && img2.data))
    {
        return 0;
    }
    cv::imshow("img1", img1);
    cv::imshow("img2", img1);
    cv::waitKey(0);
    // 用来存储关键点
    std::vector<cv::KeyPoint> keyPoints1;
    std::vector<cv::KeyPoint> keyPoints2;
    Ptr<FeatureDetector> m_fast = cv::FastFeatureDetector::create(80);	// 检测的阈值为80
    //    这里采用了可以指向任何特征检测器的泛型指针类型 cv::Ptr<cv::FeatureDetector> 。
    //    上述代码可用于各种兴趣点检测器,只需在调用函数时更换检测器即可。
    m_fast->detect(img1,keyPoints1);  // 寻找特征点
    m_fast->detect(img2,keyPoints2);  // 寻找特征点
    // 定义正方形邻域11X11
    const int nsize(11);    //邻域尺寸
    cv::Rect neighborhood(0, 0, nsize, nsize); // 11X11
    cv::Mat patch1;
    cv::Mat patch2;
    //    将一幅图像的关键点与另一幅图像的全部关键点进行比较。在第二幅图像中找出与第一幅图
    //    像中的每个关键点最相似的图像块。
    // 在第二幅图像中找出与第一幅图像中的每个关键点最匹配的
    cv::Mat result;
    std::vector<cv::DMatch> matches;
    // 针对图像一的全部关键点
    for (int i = 0; i < keyPoints1.size(); i++)
    {
        // 定义图像块
        neighborhood.x = keyPoints1[i].pt.x - nsize/2;
        neighborhood.y = keyPoints1[i].pt.y - nsize/2;
        // 如果超出图像范围，就继续处理下一个点
        // 以关键点为中心的正方形邻域
        if (neighborhood.x < 0 || neighborhood.y < 0  ||
            neighborhood.x + nsize >= img1.cols ||
            neighborhood.y + nsize >= img1.rows)
        {
            continue;
        }
        // 第一幅图像的块
        patch1 = img1(neighborhood);
        // 存放最匹配的值
        cv::DMatch bestMatch;
        // 针对第二幅图像的全部关键点
        for (int j = 0; j < keyPoints2.size(); j++)
        {
            // 定义图像块
            neighborhood.x = keyPoints2[j].pt.x - nsize/2;
            neighborhood.y = keyPoints2[j].pt.y - nsize/2;
            // 如果邻域超出图像范围，就继续处理下一个点
            if (neighborhood.x < 0 || neighborhood.y < 0 ||
                neighborhood.x + nsize >= img2.cols ||
                neighborhood.y + nsize >= img2.rows)
            {
                continue;
            }
            // 第二幅图像的块
            patch2 = img2(neighborhood);

            // 匹配两个图像块
            // 注意,这里用 cv::matchTemplate 函数来计算图像块的相似度
            cv::matchTemplate(patch1, patch2, result, cv::TM_SQDIFF);

            // 检查是否位最佳匹配
            // 找到一个可能的匹配项后,用一个 cv::DMatch 对象来表示。这个工具类存储了两个被
            // 匹配关键点的序号和它们的相似度。
            if(result.at<float>(0,0) < bestMatch.distance)
            {
                bestMatch.distance = result.at<float>(0, 0);
                bestMatch.queryIdx = i;
                bestMatch.trainIdx = j;
            }
        }
        // 添加最佳匹配
        matches.push_back(bestMatch);
    }
//    两个图像块越相似,它们对应着同一个场景点的可能性就越大。因此需要根据相似度对匹配结果进行排序
    // 提取最佳的25个匹配项
    std::nth_element(matches.begin(), matches.begin() + 25, matches.end());
    matches.erase(matches.begin() + 25, matches.end());
    // 画出匹配结果
    cv::Mat matchImage;
    cv::drawMatches(img1, keyPoints1,
                    img2, keyPoints2,
                    matches,
                    matchImage,
                    cv::Scalar(255, 255, 255),
                    cv::Scalar(255, 255, 255));
    cv::imshow("Result Image",matchImage);
    cv::waitKey(0);
    return 0;
}
// 描述并匹配局部强度值模式
int test_09_2()
{
    return 0;
}
// 用二值描述子匹配关键点      -----> 计算速度加快 
int test_09_3()
{
    using namespace cv;
    cv::Mat img1 = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    cv::Mat img2 = cv::imread("./doc/unchange/meinv02.jpeg",cv::IMREAD_COLOR);
    if(!(img1.data && img2.data))
    {
        return 0;
    }
    cv::imshow("img1", img1);
    cv::imshow("img2", img1);
    cv::waitKey(0);
    // 用来存储关键点
    std::vector<cv::KeyPoint> keyPoints1;
    std::vector<cv::KeyPoint> keyPoints2;
    // 提取描述子
    cv::Mat descriptors1;
    cv::Mat descriptors2;
    // 定义特征点检测器
     //对于 SIFT,调用 cv::SIFT::create 函数即可
    cv::Ptr<cv::Feature2D> ptrFeature2D = cv::ORB::create(60); // 大约60个特征点
    // 检测并描述关键点
    // 检测ORB特征
    ptrFeature2D -> detectAndCompute(img1, cv::noArray(), keyPoints1, descriptors1);
    ptrFeature2D -> detectAndCompute(img2, cv::noArray(), keyPoints2, descriptors2);
    // 构建匹配器
    cv::BFMatcher matcher(cv::NORM_HAMMING);    // 二值描述子一律使用Hamming规范
    // 匹配两幅图像的描述子
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);
    // 输出结果
    cv::Mat outImage;
    cv::drawMatches(img1, keyPoints1,
                    img2, keyPoints2,
                    matches,
                    outImage,
                    cv::Scalar(255, 255, 255),
                    cv::Scalar(255, 255, 255));
    cv::imshow("OUT IMAGE", outImage);
    cv::waitKey(0);
    return 0;
}

int test_09_4()
{
    return 0;
}

// 描述和匹配兴趣点 *

void ninety_option(std::string cv_num)
{
    if(cv_num == "9.1")
    {
        test_09_1();
    } else if(cv_num == "9.2")
    {
        test_09_2();
    } else if(cv_num == "9.3")
    {
        test_09_3();
    } else if(cv_num == "9.4")
    {
       test_09_4();
    } else if(cv_num == "9.5")
    {
        ;
    }
}






// 计算图像对的基础矩阵   一幅图像的极点位于所有对极线的交叉点,是另一个相机中心点的投影
int test_10_1()
{
    return 0;
}


// 用 RANSAC 算法匹配图像
int test_10_2()
{
    return 0;
}

// 计算两幅图像之间的单应矩阵
int test_10_3()
{
    return 0;
}

// 检测图像中的平面目标
int test_10_4()
{
    return 0;
}


int test_10_5()
{
    return 0;
}


/* 估算图像间的投影关系 */
void tenty_option(std::string cv_num)
{
    if(cv_num == "10.1")
    {
        test_10_1();
    } else if(cv_num == "10.2")
    {
        test_10_2();
    } else if(cv_num == "10.3")
    {
        test_10_3();
    } else if(cv_num == "10.4")
    {
       test_10_4();
    } else if(cv_num == "10.5")
    {
        test_10_5();
    }
}


int test_11_1()
{
    return 0;
}

int test_11_2()
{
    return 0;
}

int test_11_3()
{
    return 0;
}

int test_11_4()
{
    return 0;
}

int test_11_5()
{
    return 0;
}

/*  三维从建  */
void elentty_option(std::string cv_num)
{
        if(cv_num == "11.1")
    {
        test_11_1();
    } else if(cv_num == "11.2")
    {
        test_11_2();
    } else if(cv_num == "11.3")
    {
        test_11_3();
    } else if(cv_num == "11.4")
    {
       test_11_4();
    } else if(cv_num == "11.5")
    {
        test_11_5();
    }
}
// 读取视屏帧
/* 图像添加文字 */
void m_put_text1(cv::Mat img, const cv::String text, cv::Point org, int fontFace, double fontScale)
{
    cv::putText(img,text,org,fontFace,fontScale,cv::Scalar(255,255,255),2);
}
int test_12_1()
{
    // 打开视频文件
    cv::VideoCapture capture(0);
    // 检查视频是否成功打开
    if (!capture.isOpened())
        return 1;
    // 取得帧速率
    double rate= capture.get(CV_CAP_PROP_FPS);
    bool stop(false);
    cv::Mat frame; // 当前视频帧
    cv::namedWindow("Extracted Frame");
    // 根据帧速率计算帧之间的等待时间,单位为 ms
    int delay= 1000/rate;
    // 循环遍历视频中的全部帧
    while (!stop) 
    {
        // 读取下一帧(如果有)
        if (!capture.read(frame))
            break;
        std::string s_rate= "FPS: " + std::to_string(rate);
        m_put_text1(frame,cv::String(s_rate),cv::Point(10,50),1,2.5);
        cv::imshow("Extracted Frame",frame);
        // 等待一段时间,或者通过按键停止
        if (cv::waitKey(delay)>=0)
            stop= true;
    }
    // 关闭视频文件
    // 不是必须的,因为类的析构函数会调用
    capture.release();
    return 0;
}
/* 处理视屏帧 */
/* 回调处理函数 */
void canny(cv::Mat &img,cv::Mat &out)
{
    if(img.channels()==3)//灰度转换
    {
        cvtColor(img,out,cv::COLOR_BGR2GRAY);
    }
    Canny(out,out,100,200);//计算canny边缘
    threshold(out,out,128,255,cv::THRESH_BINARY_INV);//反转图像
}
int test_12_2()
{
    // 打开视频文件
    cv::VideoCapture capture(0);
    // 检查视频是否成功打开
    if (!capture.isOpened())
        return 1;
    // 取得帧速率
    double rate= capture.get(CV_CAP_PROP_FPS);
    bool stop(false);
    cv::Mat frame; // 当前视频帧
    cv::Mat canny_frame;
    cv::namedWindow("Extracted Frame");
    // 根据帧速率计算帧之间的等待时间,单位为 ms
    int delay= 1000/rate;
    // 循环遍历视频中的全部帧
    while (!stop) 
    {
        // 读取下一帧(如果有)
        if (!capture.read(frame))
            break;
        std::string s_rate= "FPS: " + std::to_string(rate);
        m_put_text1(frame,cv::String(s_rate),cv::Point(10,50),1,2.5);
        canny(frame,canny_frame);
        cv::imshow("Extracted Frame",frame);
        cv::imshow("canny Frame",canny_frame);
        // 等待一段时间,或者通过按键停止
        if (cv::waitKey(delay)>=0)
            stop= true;
    }
    // 关闭视频文件
    // 不是必须的,因为类的析构函数会调用
    capture.release();
    return 0;
}


/* 写入视屏帧 */
int test_12_3()
{
    // 打开视频文件
    cv::VideoCapture capture(0);
    cv::VideoWriter v_w;
    cv::Mat img;
    capture>>img;//获取图像
    // 检查视频是否成功打开
    if (!capture.isOpened())
        return 1;
    // 取得帧速率
    double rate= capture.get(CV_CAP_PROP_FPS);
    bool stop(false);
    int coder=cv::VideoWriter::fourcc('M','J','P','G');//选择编码格式
    v_w.open("./doc/output_video02.avi",coder,rate,img.size(),0);//创建保存视频文件的视频流
    if(!v_w.isOpened()){
        std::cout<<"打开视频文件失败，请确认是否为合法输入"<< std::endl;
        return -1;
    }
    cv::Mat frame; // 当前视频帧
    cv::Mat canny_frame;
    cv::namedWindow("Extracted Frame");
    // 根据帧速率计算帧之间的等待时间,单位为 ms
    int delay= 1000/rate;
    // 循环遍历视频中的全部帧
    while (!stop) 
    {
        // 读取下一帧(如果有)
        if (!capture.read(frame))
            break;
        std::string s_rate= "FPS: " + std::to_string(rate);
        m_put_text1(frame,cv::String(s_rate),cv::Point(10,50),1,2.5);
        canny(frame,canny_frame);
        v_w.write(canny_frame);   // 处理后的帧写入文件
        cv::imshow("Extracted Frame",frame);
        cv::imshow("canny Frame",canny_frame);
        // 等待一段时间,或者通过按键停止
        if (cv::waitKey(delay)>=0)
            stop= true;
    }
    // 关闭视频文件
    // 不是必须的,因为类的析构函数会调用
    capture.release();
    v_w.release();
    return 0;
}
/* 提取视屏中的前景物 */
/* 
用固定位置的像机拍摄时,背景部分基本上是保持不变的。这种情况下,我们关注的是场景中的移动物体。
为了提取这些前景物体,我们需要构建一个背景模型,然后将模型与当前帧做比较,检测出所有的前景物体。
*/
/* 每一帧和背景帧进行比较 */

Mat frame; //current frame
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
char keyboard; //input from keyboard
void processVideo()
{
    VideoCapture capture(0);
    if(!capture.isOpened()){
        exit(EXIT_FAILURE);
    }
    //read input data. ESC or 'q' for quitting
    keyboard = 0;
    while( keyboard != 'q' && keyboard != 27 ){
        //read the current frame
        if(!capture.read(frame)) {
            cerr << "Unable to read next frame." << endl;
            cerr << "Exiting..." << endl;
            exit(EXIT_FAILURE);
        }
        //update the background model
        pMOG2->apply(frame, fgMaskMOG2);
        //get the frame number and write it on the current frame
        stringstream ss;
        rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
                  cv::Scalar(255,255,255), -1);
        ss << capture.get(CAP_PROP_POS_FRAMES);
        string frameNumberString = ss.str();
        putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
                FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
        //show the current frame and the fg masks
        imshow("Frame", frame);
        imshow("FG Mask MOG 2", fgMaskMOG2);
        //get the input from the keyboard
        keyboard = (char)waitKey( 30 );
    }
    //delete capture object
    capture.release();
}
void processImages(char* firstFrameFilename)
{
     //read the first file of the sequence
    frame = imread(firstFrameFilename);
    if(frame.empty()){
        //error in opening the first image
        cerr << "Unable to open first image frame: " << firstFrameFilename << endl;
        exit(EXIT_FAILURE);
    }
    //current image filename
    string fn(firstFrameFilename);
    //read input data. ESC or 'q' for quitting
    keyboard = 0;
    while( keyboard != 'q' && keyboard != 27 ){
        //update the background model
        pMOG2->apply(frame, fgMaskMOG2);
        //get the frame number and write it on the current frame
        size_t index = fn.find_last_of("/");
        if(index == string::npos) {
            index = fn.find_last_of("\\");
        }
        size_t index2 = fn.find_last_of(".");
        string prefix = fn.substr(0,index+1);
        string suffix = fn.substr(index2);
        string frameNumberString = fn.substr(index+1, index2-index-1);
        istringstream iss(frameNumberString);
        int frameNumber = 0;
        iss >> frameNumber;
        rectangle(frame, cv::Point(10, 2), cv::Point(100,20),
                  cv::Scalar(255,255,255), -1);
        putText(frame, frameNumberString.c_str(), cv::Point(15, 15),
                FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
        //show the current frame and the fg masks
        imshow("Frame", frame);
        imshow("FG Mask MOG 2", fgMaskMOG2);
        //get the input from the keyboard
        keyboard = (char)waitKey( 30 );
        //search for the next image in the sequence
        ostringstream oss;
        oss << (frameNumber + 1);
        string nextFrameNumberString = oss.str();
        string nextFrameFilename = prefix + nextFrameNumberString + suffix;
        //read the next frame
        frame = imread(nextFrameFilename);
        if(frame.empty()){
            //error in opening the next image in the sequence
            cerr << "Unable to open image frame: " << nextFrameFilename << endl;
            exit(EXIT_FAILURE);
        }
        //update the path of the current frame
        fn.assign(nextFrameFilename);
    }
}

// 有问题的
int test_12_4()
{
    return 0;
    //create GUI windows
    namedWindow("Frame");
    namedWindow("FG Mask MOG 2");
    //create Background Subtractor objects
    pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach
    //input data coming from a video
    processVideo();
    // else if(strcmp(argv[1], "-img") == 0) {
        //input data coming from a sequence of images
        processImages("0");
    // }
    // else {
        // // error in reading input parameters
        // cerr <<"Please, check the input parameters." << endl;
        // cerr <<"Exiting..." << endl;
        // return EXIT_FAILURE;
    // }
    //destroy GUI windows
    destroyAllWindows();
    return EXIT_SUCCESS;
}

int test_12_5()
{
    return 0;
}


/* 处理视频帧序列 */
void shierty_option(std::string cv_num)
{
        if(cv_num == "12.1")
    {
        test_12_1();
    } else if(cv_num == "12.2")
    {
        test_12_2();
    } else if(cv_num == "12.3")
    {
        test_12_3();
    } else if(cv_num == "12.4")
    {
       test_12_4();
    } else if(cv_num == "12.5")
    {
        test_12_5();
    }
}


/* 流光法跟踪视屏中的特征点 */
//帧处理基类  
class FrameProcessor
{
public:
    virtual void process(Mat &input,Mat &ouput)=0;
};

class FeatureTracker:public FrameProcessor
{
    Mat gray;//当前灰度图像
    Mat gray_prev;//之前灰度图像
    //两幅图像间跟踪的特征点 0->1
    vector<Point2f>points[2];
    // 跟踪的点的初始位置
    vector<Point2f>initial;
    vector<Point2f>features;//检测到的特征
    int max_count;//需要跟踪的最大特征数目
    double qlevel;//特征检测中的质量等级
    double minDist;//两点之间的最小距离
    vector<uchar>status;//检测到的特征的状态
    vector<float>err;//跟踪过程中的错误
public:
    FeatureTracker():max_count(500),qlevel(0.01),minDist(10.){}

    void process(Mat &frame,Mat &output)
    {
        //转换为灰度图像
        cvtColor(frame,gray,CV_BGR2GRAY);
        frame.copyTo(output);
        //1.如果需要添加新的特征点
        if(addNewPoints())
        {
            //进行检测
            detectFeaturePoints();
            //添加检测到的特征到当前跟踪的特征中
            points[0].insert(points[0].end(),features.begin(),features.end());
            initial.insert(initial.end(),features.begin(),features.end());
        }
        //对于序列中的第一幅图像
        if(gray_prev.empty())
        {
            gray.copyTo(gray_prev);
        }
        //2.跟踪特征点
        calcOpticalFlowPyrLK(
            gray_prev,gray,//两幅连续图
            points[0],//图1中的输入点坐标
            points[1],//图2中的输出点坐标
            status,//跟踪成果
            err);//跟踪错误
        //2.遍历所有跟踪的点进行筛选
        int k=0;
        for(int i=0;i<points[1].size();i++)
        {
            //是否需要保留该点？
            if(acceptTrackedPoint(i))
            {
                //进行保留
                initial[k]=initial[i];
                points[1][k++]=points[1][i];
            }
        }
        //去除不成功的点
        points[1].resize(k);
        initial.resize(k);
        //3.处理接收的跟踪点
        handleTrackedPoints(frame,output);
        //4.当前帧的点和图像变为前一帧的点和图像
        swap(points[1],points[0]);
        swap(gray_prev,gray);
    }

    //检测特征点
    void detectFeaturePoints()
    {
        //检测特征
        goodFeaturesToTrack(gray,//图像
                            features,//检测到的特征
                            max_count,//特征的最大数目
                            qlevel,//质量等级
                            minDist);//两个特征之间的最小距离
    }

    //是否需要添加新的点
    bool addNewPoints()
    {
        //如果点的数量太少
        return points[0].size()<=10;
    }

    //决定哪些点应该跟踪
    bool acceptTrackedPoint(int i)
    {
        return status[i]&&
            //如果它移动了
            (abs(points[0][i].x-points[1][i].x))+
            (abs(points[0][i].y-points[1][i].y))>2;
    }

    //处理当前跟踪的点
    void handleTrackedPoints(Mat &frame,Mat &output)
    {
        //遍历所有跟踪点
        for(int i=0;i<points[1].size();i++)
        {
            //绘制直线和圆
            line(output,
                 initial[i],//初始位置
                 points[1][i],//新位置
                 Scalar(255,255,255));
            circle(output,points[1][i],3,Scalar(255,255,255),-1);
        }
    }
};

class VideoProcessor
{
private:

    VideoCapture caputure;  
    //输出文件名  
    string Outputfile;
    int currentIndex;  
    int digits;  
    string extension;  
    FrameProcessor *frameprocessor;  
    //图像处理函数指针  
    void (*process)(Mat &,Mat &);  
    bool callIt;  
    string WindowNameInput;  
    string WindowNameOutput;  
    //延时  
    int delay;  
    long fnumber;  
    //第frameToStop停止  
    long frameToStop;  
    //暂停标志  
    bool stop;  
    //图像序列作为输入视频流  
    vector<string> images;  
    //迭代器  
public:  
    VideoProcessor():callIt(true),delay(0),fnumber(0),stop(false),digits(0),frameToStop(-1){}


    //设置图像处理函数  
    void setFrameProcessor(void (*process)(Mat &,Mat &)){  
        frameprocessor = 0;  
        this->process = process;  
        CallProcess ();  
    }  
    //打开视频  
    bool setInput(int filename){  
        fnumber = 0;  
        //若已打开，释放重新打开  
        caputure.release ();  
        return caputure.open (filename);  
    }  
    //设置输入视频播放窗口  
    void displayInput(string wn){  
        WindowNameInput = wn;  
        namedWindow (WindowNameInput);  
        }  
    //设置输出视频播放窗口  
    void displayOutput(string wn){  
        WindowNameOutput = wn;  
        namedWindow (WindowNameOutput);  
        }  
    //销毁窗口  
    void dontDisplay(){  
        destroyWindow (WindowNameInput);  
        destroyWindow (WindowNameOutput);  
        WindowNameInput.clear ();  
        WindowNameOutput.clear ();  
    }  

    //启动  
    void run(){  
        Mat frame;  
        Mat output;  
        if(!isOpened())  
        return;  
        stop = false;  
        while(!isStopped()){  
        //读取下一帧  
            if(!readNextFrame(frame))  
            break;  
            if(WindowNameInput.length ()!=0)  
            imshow (WindowNameInput,frame);  
            //处理该帧  
            if(callIt){  
                if(process)  
                process(frame,output);  
                else if(frameprocessor)  
                frameprocessor->process (frame,output);  
            }  
            else{  
                output = frame;  
            }  

            if(WindowNameOutput.length ()!=0)  
                imshow (WindowNameOutput,output);  
            //按键暂停，继续按键继续  
            if(delay>=0&&waitKey (delay)>=0)  
                waitKey(0);  
            //到达指定暂停键，退出  
            if(frameToStop>=0&&getFrameNumber()==frameToStop)  
                stopIt();  
        }  
    }  
    //暂停键置位  
    void stopIt(){  
        stop = true;  
    }  
    //查询暂停标志位  
    bool isStopped(){  
        return stop;  
    }  
    //返回视频打开标志  
    bool isOpened(){  
        return  caputure.isOpened ()||!images.empty ();  
    }  
    //设置延时  
    void setDelay(int d){  
        delay = d;  
    }  
    //读取下一帧  
    bool readNextFrame(Mat &frame){  
        if(images.size ()==0)  
        return caputure.read (frame);  
        else{  
            if(itImg!=images.end()){  
                frame = imread (*itImg);  
                itImg++;  
            return frame.data?1:0;  
            }  
        else  
        return false;  
        }  
    }  

    void CallProcess(){  
        callIt = true;  
    }  
    void  dontCallProcess(){  
        callIt = false;  
    }  
    //设置停止帧  
    void stopAtFrameNo(long frame){  
        frameToStop = frame;  
    }  
    // 获得当前帧的位置  
    long getFrameNumber(){  
        long fnumber = static_cast<long>(caputure.get ((CV_CAP_PROP_POS_FRAMES)));  
        return fnumber;  
    }  

    //获取帧率  
    double getFrameRate(){  
        return caputure.get(CV_CAP_PROP_FPS);  
    }
    vector<string>::const_iterator itImg;
    bool setInput (const vector<string> &imgs){  
        fnumber = 0;  
        caputure.release ();  
        images = imgs;  
        itImg = images.begin ();  
        return true;  
    }  

    void  setFrameProcessor(FrameProcessor *frameprocessor){  
        process = 0;  
        this->frameprocessor = frameprocessor;  
        CallProcess ();  
    }     
};  
int test_13_1()
{
    //创建视频处理器实例
    VideoProcessor processor;
    //创建特征跟踪器实例
    FeatureTracker tracker;
    //打开视频文件
    processor.setInput(0);
    //设置帧处理器对象
    processor.setFrameProcessor(&tracker);
    //声明显示窗口
    processor.displayOutput("Tracked Features");
    //以原始帧率播放视频
    processor.setDelay(1000./processor.getFrameRate());
    //开始处理过程
    processor.run();
    return 0;
}
/* 估算光流 */
int test_13_2()
{
    return 0;
}
/* 跟踪视频中的光流 */
int test_13_3()
{
    return 0;
}

int test_13_4()
{
    return 0;
}

int test_13_5()
{
    return 0;
}


/*  跟踪运动目标 */
void shisanty_option(std::string cv_num)
{
    if(cv_num == "13.1")
    {
        test_13_1();
    } else if(cv_num == "13.2")
    {
        test_13_2();
    } else if(cv_num == "13.3")
    {
        test_13_3();
    } else if(cv_num == "13.4")
    {
       test_13_4();
    } else if(cv_num == "13.5")
    {
        test_13_5();
    }
}


















